# !/usr/bin/env python
# -*- coding: utf-8 -*-

import math

'''
在协同过滤算法中，需要计算用户或物品之间的相似度。常用的相似度计算方法有：

皮尔逊相关系数（Pearson Correlation Coefficient）
余弦相似度（Cosine Similarity）
Jaccard相似度（Jaccard Similarity）
'''

'''
余弦相似度衡量了两个向量的夹角，夹角越小越相似。首先从集合的角度描述余弦相似度，相比于Jaccard公式来说就是分母有差异，
不是两个用户交互商品的并集的数量，而是两个用户分别交互的商品数量的乘积
'''

user_movie_ratings = {
    'Alice': {'Movie1': 5, 'Movie2': 3, 'Movie3': 4},
    'Bob': {'Movie1': 3, 'Movie2': 1, 'Movie3': 5, 'Movie4': 3},
    'Carol': {'Movie1': 4, 'Movie2': 2, 'Movie3': 4, 'Movie5': 7, 'Movie6': 4},
}

movie_user_ratings = {
    'Movie1': {'Alice': 5, 'Bob': 3, 'Carol': 4},
    'Movie2': {'Alice': 3, 'Carol': 2},
    'Movie3': {'Alice': 4, 'Bob': 5, 'Carol': 4},
    'Movie4': {'Bob': 3},
    'Movie5': {'Carol': 7},
    'Movie6': {'Carol': 6},
}


# 余弦相似度
def cosine_similarity(a, b):
    numerator = sum([a[key] * b[key] for key in a if key in b])
    denominator = math.sqrt(sum([a[key] ** 2 for key in a])) * math.sqrt(sum([b[key] ** 2 for key in b]))
    return numerator / denominator


def user_recommend_movies(user):
    # 计算目标用户与其他用户的相似度
    similarities = {other_user: cosine_similarity(user_movie_ratings[user], user_movie_ratings[other_user]) for
                    other_user in user_movie_ratings if other_user != user}

    print(similarities)
    # 按相似度降序排列用户
    sorted_users = sorted(similarities, key=similarities.get, reverse=True)
    print(sorted_users)

    # 找到与目标用户最相似的用户
    most_similar_user = sorted_users[0]

    # 找到最相似用户喜欢的电影，但目标用户未看过的电影
    movies = {movie: rating for movie, rating in user_movie_ratings[most_similar_user].items() if
              movie not in user_movie_ratings[user]}
    # 按评分降序排列推荐电影
    sorted_recommended_movies = sorted(movies, key=movies.get, reverse=True)
    return sorted_recommended_movies


def item_recommend_movies(movie):
    # 计算目标电影与其他电影的相似度
    similarities = {other_movie: cosine_similarity(movie_user_ratings[movie], movie_user_ratings[other_movie]) for
                    other_movie in movie_user_ratings if other_movie != movie}

    print(similarities)
    # 按相似度降序排列用户
    sorted_movies = sorted(similarities, key=similarities.get, reverse=True)
    print(sorted_movies)

    # 找到与目标用户最相似电影
    most_similar_movie = sorted_movies[0]

    # 找到看过最相似电影的用户
    users = {user: rating for user, rating in movie_user_ratings[most_similar_movie].items() if
             user not in movie_user_ratings[movie]}
    # 按评分降序排列推荐电影
    sorted_recommended_users = sorted(users, key=users.get, reverse=True)
    return sorted_recommended_users


if __name__ == '__main__':
    # 为Alice推荐电影
    recommended_movies = user_recommend_movies('Alice')
    print(f"Recommended movies for Alice: {recommended_movies}")

    # 为Movie2推荐用户
    recommended_users = item_recommend_movies('Movie2')
    print(f"Recommended users for Movie1: {recommended_users}")
